#!/usr/bin/python

import os, traceback, sys, time
from xml.dom import minidom

INPUT_FILE = 'input'
OUTPUT_FILE = 'output'
HISTORY_FILE = 'history'
ERROR_FILE = 'error'

def make_xml_node(doc, obj, node_name):
	node = doc.createElement(node_name)
	properties = filter(lambda x: x[:2] != '__', dir(obj))

	for property_name in properties:
		property = getattr(obj, property_name)

		if hasattr(property, '__iter__'):
			text = ' '.join([str(item) for item in property])
		else:
			text = str(getattr(obj, property_name))

		property_node = doc.createElement(property_name)
		property_node.appendChild(doc.createTextNode(text))
		node.appendChild(property_node)

	return node

class World(list):
	def __str__(self):
		return '\n'.join([str(item) for item in self])

	def toxmldoc(self):
		doc = minidom.getDOMImplementation().createDocument(None, "world", None)

		for item in self:
			entity = make_xml_node(doc, item, "entity")
			doc.documentElement.appendChild(entity)

		return doc

	def toxml(self):
		return self.toxmldoc().toxml()

	def do(self, elapsed_time):
		updated = False
		
		self = filter(lambda x: not x.delete_me, self)

		for entity in world:
			result = entity.do(elapsed_time)
			updated |= result

		return updated

class Entity(object):
	delete_me = False

	def __init__(self, id):
		self.id = id
		self.pos = [0, 0, 0]
		self.moveMethods = [None, None, None]
		self.since = 0

	def __str__(self):
		return self.id + ' at ' + str(self.pos)

	def do(self, elapsed_time):
		updated = False

		for i in range(len(self.moveMethods)):
			move_method = self.moveMethods[i]
			if move_method:
				self.since += elapsed_time
				self.pos[i] = move_method(self.since)
				updated = True

		return updated

class Block(Entity):
	count = 0

	def __init__(self):
		Block.count += 1
		super(Block, self).__init__('block-' + str(Block.count))
		self.pos = [-80.0, 0, 0]
		self.moveMethods[0] = lambda t: t + 0.1

	def do(self, elapsed_time):
		if self.pos[0] > 30:
			self.delete_me = True

		return super(Block, self).do(elapsed_time)

world = World()
last_time = None
command = None

def on_tick(user_input = None):
	global last_time

	try:
		new_time = time.time()

		if not last_time:
			elapsed_time = 0
		else:
			elapsed_time = new_time - last_time

		result = _on_tick(user_input, elapsed_time)

		last_time = new_time

		return result
	except:
		traceback.print_exc()

	return None

def process_user_input(world, user_input, elapsed_time):
	if len(world) <= 0 or not user_input:
		return False

	xml = minidom.parseString(user_input)

	user_input_xml = xml.getElementsByTagName('end')[0].getElementsByTagName('input')[0]
	player = world[0]

	for user_key in user_input_xml.childNodes:
		if not hasattr(user_key, "tagName"):
			continue

		# j: up
		# k: down
		# h: left
		# l: right
		move_factor = elapsed_time * 4
		move_input_map = {'j': [move_factor, 0, 0], 'k': [-move_factor, 0, 0], 'h': [0, -move_factor, 0], 'l': [0, move_factor, 0]}
		method_codes = ['lambda t: %f + %f' % (x[0], x[1]) for x in zip(player.pos, move_input_map[user_key.tagName])]
		player.moveMethods = [eval(code) for code in method_codes]

		return True

	return False

def run_commands(commands):
	for command in commands:
		if command:
			exec command

def process_console_input(world):
	if not os.path.isfile('input'):
		return False

	fp = open('input', 'r')
	commands = fp.read().split('\n')

	sys_stderr = sys.stderr
	sys.stderr = open('error', 'w')

	sys_stdout = sys.stdout
	sys.stdout = open('output', 'w')

	try:
		run_commands(commands)
	except:
		traceback.print_exc()

	sys.stdout.close()
	sys.stdout = sys_stdout

	sys.stderr.close()
	sys.stderr = sys_stderr

	fp.close()
	os.remove('input')

	return True

def end_exit():
	global command
	command = 'exit'

def load_history():
	lines = open(HISTORY_FILE).readlines()[:-1]
	fp = open(HISTORY_FILE, 'w')
	fp.writelines(lines)
	fp.close()

	run_commands(lines)

def _on_tick(user_input = None, elapsed_time = 0):
	global world

	updated = process_user_input(world, user_input, elapsed_time)
	updated |= process_console_input(world)
 	updated |= world.do(elapsed_time)

	if updated:
		global command
		doc = world.toxmldoc()
		if command:
			node = doc.createElement('command')
			node.appendChild(doc.createTextNode(command))
			doc.documentElement.appendChild(node)
			command = ''
		return doc.toxml()

	return ''
